package master

import (
	"crontab/common"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/samuel/go-zookeeper/zk"
	"time"
)

type JobMgr struct {
	client *zk.Conn
}

//全局单例
var (
	G_JobMgr *JobMgr
)

func InitJobMgr() (err error) {

	con, connChan, err := zk.Connect(G_Config.ZKEndPoint, time.Duration(G_Config.ZKConnectTimeout)*time.Millisecond)
	if err != nil {

		return err
	}

	for {
		isConnect := false
		select {
		case connEvent := <-connChan:
			if connEvent.State == zk.StateConnected {
				isConnect = true
				fmt.Println("connect to zookeeper server success")
			}
		case _ = <-time.After(time.Duration(G_Config.ZKConnectTimeout) * time.Millisecond):
			return errors.New("connect to zookeeper server timeout")

		}
		if isConnect {

			break
		}
	}
	G_JobMgr = &JobMgr{
		client: con,
	}
	//创建根节点,"/cron/jobs"
	if _, err := G_JobMgr.CreatePath(common.JOB, []byte(""), 0); err == nil {
		if _, err := G_JobMgr.CreatePath(common.JOB_SVAE_PATH, []byte(""), 0); err == nil {
			if _, err := G_JobMgr.CreatePath(common.JOB_KILLER_PATH, []byte(""), 0); err == nil {
				if _, err := G_JobMgr.CreatePath(common.JOB_WATCHER_PATH, []byte(""), 0); err == nil {
					if _, err := G_JobMgr.CreatePath(common.JOB_WATCHER_PATH+"/"+"curd", []byte(""), 0); err == nil {
						if _, err := G_JobMgr.CreatePath(common.JOB_WATCHER_PATH+"/"+"killer", []byte(""), 0); err == nil {
							return nil
						}
					}
				}
			}
		}

	}

	G_JobMgr.client.Close()

	return err
}
func (jobMgr *JobMgr) IsExsist(path string) (b bool, err error) {
	b, _, err = jobMgr.client.Exists(path)
	return b, err
}

func (jobMgr *JobMgr) CreatePath(path string, data []byte, flag int32) (str string, err error) {

	exists, err := jobMgr.IsExsist(path)
	if err != nil {
		return "", err
	}
	if !exists {
		str, err = jobMgr.client.Create(path, data, flag, zk.WorldACL(zk.PermAll))
		if err != nil && err != zk.ErrNodeExists {
			return "", err
		}
	}
	return path, err
}

func (jobMgr *JobMgr) SetData(path string, data []byte) (err error) {
	if _, err = jobMgr.CreatePath(path, data, 0); err == zk.ErrNodeExists || err == nil {

		jobMgr.client.Set(path, data, -1)
		return nil
	}

	return err
}

func (jobMgr *JobMgr) DeleteData(path string) (err error) {
	err = jobMgr.client.Delete(path, -1)

	return err
}

func (jobMgr *JobMgr) GetData(path string) (value []byte, err error) {
	if value, _, err = jobMgr.client.Get(path); err != nil {
		value = nil
	}

	return value, err
}

func (jobMgr *JobMgr) GetChildrens(path string) (value []string, err error) {

	if value, _, err = jobMgr.client.Children(path); err != nil {
		value = nil
	}

	return value, err
}

func (jobMgr *JobMgr) SaveJob(job *common.Job) (oldJob *common.Job, err error) {
	//把任务保存到指定路径下
	var (
		jobPath  string
		jobValue []byte
		oldValue []byte
	)
	jobPath = common.JOB_SVAE_PATH + "/" + job.Name
	//任务信息
	if jobValue, err = json.Marshal(job); err != nil {
		return
	}
	oldValue, err = jobMgr.GetData(jobPath)

	if err = jobMgr.SetData(jobPath, jobValue); err != nil {
		return
	}
	if err = jobMgr.SetData(common.JOB_WATCHER_PATH+"/"+"curd", jobValue); err != nil {
		return
	}
	if oldValue != nil {
		if err = json.Unmarshal(oldValue, &oldJob); err != nil {
			err = nil
			return
		}
	}

	//如果更新成功
	return oldJob, err
}

func (jobMgr *JobMgr) DeleteJob(jobPath string) (oldJob *common.Job, err error) {
	//把任务保存到指定路径下
	var (
		oldValue []byte
	)

	//先要进行查询任务信息
	oldValue, err = jobMgr.GetData(jobPath)
	if err != nil {
		return nil, err
	}

	if err = jobMgr.DeleteData(jobPath); err != nil {
		return nil, err
	}
	if err = jobMgr.SetData(common.JOB_WATCHER_PATH+"/"+"curd", oldValue); err != nil {
		return
	}
	if oldValue != nil {
		if err = json.Unmarshal(oldValue, &oldJob); err != nil {
			err = nil
			return
		}
	}

	//如果更新成功
	return oldJob, err
}

func (jobMgr *JobMgr) ListJob(path string, offset, limit int) (jobs []*common.Job, cnt int, err error) {
	//把任务保存到指定路径下
	var (
		oldJob   *common.Job
		oldValue []string
		start    int
		end      int
		page     []string
		jobItem  []byte
	)
	if path == "" {
		path = common.JOB_SVAE_PATH
	}

	//先要进行查询任务信息
	if oldValue, err = jobMgr.GetChildrens(path); err != nil {
		return nil, 0, err
	}
	if oldValue != nil {
		if offset <= 1 {
			offset = 1
		}
		if limit <= 0 {
			limit = 10
		}
		cnt = len(oldValue)
		start = (offset - 1) * limit
		if start < 0 {
			start = 0
		}
		if start > cnt {
			start = cnt
		}
		end = start + limit
		if end > cnt {
			end = cnt
		}
		/*fmt.Println("oldValue=",oldValue)
		fmt.Println("start=",start)
		fmt.Println("end=",end)
		fmt.Println("limit=",limit)*/
		page = oldValue[start:end]
		/*fmt.Println("page=",page)*/
		for _, value := range page {
			if jobItem, err = jobMgr.GetData(common.JOB_SVAE_PATH + "/" + value); err == nil {
				oldJob = &common.Job{}
				if err = json.Unmarshal(jobItem, &oldJob); err != nil {
					err = nil
					continue
				} else {
					jobs = append(jobs, oldJob)
				}
			} else {
				return nil, 0, err
			}

		}

	}

	//如果更新成功
	return jobs, cnt, err
}

//原理就是想/cron/killer中添加一个任务
func (jobMgr *JobMgr) KillJob(name string) (err error) {
	var (
		oldJob   *common.Job
		jobValue []byte
	)

	if oldJob, err = jobMgr.DeleteJob(common.JOB_SVAE_PATH + "/" + name); err != nil {
		return err
	}
	if jobValue, err = json.Marshal(oldJob); err != nil {
		return
	}
	err = jobMgr.SetData(common.JOB_KILLER_PATH+"/"+name, jobValue)
	err = jobMgr.SetData(common.JOB_WATCHER_PATH+"/"+"killer", jobValue)

	return
}

func (jobMgr *JobMgr) ZkStateStringFormat(s *zk.Stat) string {
	return fmt.Sprintf("Czxid:%d\nMzxid: %d\nCtime: %d\nMtime: %d\nVersion: %d\nCversion: %d\nAversion: %d\nEphemeralOwner: %d\nDataLength: %d\nNumChildren: %d\nPzxid: %d\n",
		s.Czxid, s.Mzxid, s.Ctime, s.Mtime, s.Version, s.Cversion, s.Aversion, s.EphemeralOwner, s.DataLength, s.NumChildren, s.Pzxid)
}
